package com.tigerlee.candle.spreadsheet;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerPNames;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import com.tigerlee.candle.util.Constants;

import android.util.Log;

public class MyHttpRequest{

    private DefaultHttpClient httpClient;
    private HttpContext localContext;
    private String ret;

    private HttpResponse response = null;
    private HttpPost httpPost = null;
    private HttpGet httpGet = null;
    
    private int connectionTimeOut = 3000;
    private int socketTimeOut = 3000;
    private int maxNrRetries = 3;
    
    private static String TAG = "CandleClient-HttpRequest";
    private static class SocketTimeOutRetryHandler implements HttpRequestRetryHandler {

        private final HttpParams httpParams;
        private final int maxNrRetries;

        /**
         * @param httpParams    HttpParams that will be used in the HttpRequest.
         * @param maxNrRetries  Max number of times to retry Request on failure due to SocketTimeOutException.
         */
        private SocketTimeOutRetryHandler(HttpParams httpParams, int maxNrRetries) {
            this.httpParams = httpParams;
            this.maxNrRetries = maxNrRetries;
        }

        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
            if (exception instanceof SocketTimeoutException) {
                if (executionCount <= maxNrRetries) {

                    if (httpParams != null) {
                        final int newSocketTimeOut = HttpConnectionParams.getSoTimeout(httpParams) * 2;
                        HttpConnectionParams.setSoTimeout(httpParams, newSocketTimeOut);
                        if(Constants.LOG_VISIBLE)
                        	Log.d(TAG, "SocketTimeOut - increasing time out to " + newSocketTimeOut + " millis and trying again");
                    } else {
                    	if(Constants.LOG_VISIBLE)
                    		Log.d(TAG, "SocketTimeOut - no HttpParams, cannot increase time out. Trying again with current settings");
                    }

                    return true;
                }

                Log.d(TAG, "SocketTimeOut but exceeded max number of retries : " + maxNrRetries);
            }

            return false;  //To change body of implemented methods use File | Settings | File Templates.
        }
    }

    public MyHttpRequest(){
    	        
    	HttpParams myParams = new BasicHttpParams();
        myParams.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109);
        myParams.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
        myParams.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
        
        HttpConnectionParams.setConnectionTimeout(myParams, connectionTimeOut);
        HttpConnectionParams.setSoTimeout(myParams, socketTimeOut);
        HttpConnectionParams.setSocketBufferSize(myParams, 8192);
    	
    	final SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", new PlainSocketFactory(), 80));
        registry.register(new Scheme("https", (new FakeSocketFactory()), 443));
        
        
        final ClientConnectionManager clientConnectionManager = new ThreadSafeClientConnManager(myParams, registry);
        httpClient = new DefaultHttpClient(clientConnectionManager, myParams);

        final HttpRequestRetryHandler retryHandler = new SocketTimeOutRetryHandler(myParams, maxNrRetries);
        httpClient.setHttpRequestRetryHandler(retryHandler);
               
        localContext = new BasicHttpContext();    
    }

    public void clearCookies() {
        httpClient.getCookieStore().clear();
    }

    public void abort() {
        try {
            if (httpClient != null) {
                System.out.println("Abort.");
                httpPost.abort();
            }
        } catch (Exception e) {
            System.out.println(TAG + e);
        }
    }

    public String sendPost(String url, String data) {
        return sendPost(url, data, null);
    }

    public String sendJSONPost(String url, JSONObject data) {
        return sendPost(url, data.toString(), "application/json");
    }

    public String sendPost(String url, String data, String contentType) {
        ret = null;

        httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2109);

        httpPost = new HttpPost(url);
        response = null;

        StringEntity tmp = null;        

        if(Constants.LOG_VISIBLE) 
        	Log.d(TAG, "Setting httpPost headers");

        httpPost.setHeader("User-Agent", "SET YOUR USER AGENT STRING HERE");
        httpPost.setHeader("Accept", "text/html,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");

        if (contentType != null) {
            httpPost.setHeader("Content-Type", contentType);
        } else {
            httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
        }

        try {
            tmp = new StringEntity(data,"UTF-8");
        } catch (UnsupportedEncodingException e) {
        	if(Constants.LOG_VISIBLE) Log.e(TAG, "HttpUtils : UnsupportedEncodingException : "+e);
        }

        httpPost.setEntity(tmp);

        if(Constants.LOG_VISIBLE) 
        	Log.d(TAG, url + "?" + data);

        try {
            response = httpClient.execute(httpPost,localContext);

            if (response != null) {
                ret = EntityUtils.toString(response.getEntity());
            }
        } catch (Exception e) {
        	if(Constants.LOG_VISIBLE) Log.e(TAG, "HttpUtils: " + e);
        }

        if(Constants.LOG_VISIBLE) Log.d(TAG, "Returning value:" + ret);

        return ret;
    }

    public void sendGet(String url) {
        httpGet = new HttpGet(url);  

        try {
            response = httpClient.execute(httpGet);  
        } catch (Exception e) {
        	if(Constants.LOG_VISIBLE) Log.e(TAG, e.getMessage());
        }
    }
    
    public InputStream getHttpStream(String urlString) throws IOException, NullPointerException {
        InputStream in = null;
        this.sendGet(urlString);
        try{
        	in = this.response.getEntity().getContent();
        }catch (IOException e) {
        	e.printStackTrace();
		}catch (NullPointerException e){
			e.printStackTrace();
		}
        return in;
    }


    /*
    public InputStream getHttpStream(String urlString) throws IOException {
        InputStream in = null;
        int response = -1;

        URL url = new URL(urlString); 
        URLConnection conn = url.openConnection();

        if (!(conn instanceof HttpURLConnection))                     
            throw new IOException("Not an HTTP connection");

        try{
            HttpURLConnection httpConn = (HttpURLConnection) conn;
            httpConn.setAllowUserInteraction(false);
            httpConn.setInstanceFollowRedirects(true);
            httpConn.setRequestMethod("GET");
            httpConn.connect(); 

            response = httpConn.getResponseCode();                 

            if (response == HttpURLConnection.HTTP_OK) {
                in = httpConn.getInputStream();                                 
            }                     
        } catch (Exception e) {
            throw new IOException("Error connecting");            
        } // end try-catch

        return in;     
    }
    */
    public void setConnectionTimeOut(int connectionTimeOut) {
        this.connectionTimeOut = connectionTimeOut;
    }

    public void setSocketTimeOut(int socketTimeOut) {
        this.socketTimeOut = socketTimeOut;
    }

    /**
     * The default number of retries is 3.
     *
     * @param maxNrRetries  Max number of times to retry Request on failure due to SocketTimeOutException.
     */
    public void setMaxNrRetries(int maxNrRetries) {
        this.maxNrRetries = maxNrRetries;
    }
    
	public void sendPost(URL reportUrl, Map<String, String> parameters) throws IOException{
		// TODO Auto-generated method stub
        final HttpPost httpPost = getHttpPost(reportUrl, parameters);

        if(Constants.LOG_VISIBLE){
        	Log.e(TAG, "Sending request to " + reportUrl);
        	Log.e(TAG, "HttpPost params : ");
        	for (final Object key : parameters.keySet()) {
        		
            	Log.e(TAG, " key : '" + key + "'    value: '" + parameters.get(key) + "'");
            }
        }
        

        // TODO Consider using a RequestRetryHandler and if its a SocketTimeoutException to up the SocketTimeout and try again.
        // See http://stackoverflow.com/questions/693997/how-to-set-httpresponse-timeout-for-android-in-java
        // I think SocketTimeOut while waiting for response may be the cause of the multiple crash reports () - I
        final HttpResponse response = httpClient.execute(httpPost, new BasicHttpContext());
        if (response != null) {
            final StatusLine statusLine = response.getStatusLine();
            if (statusLine != null) {
                final String statusCode = Integer.toString(response.getStatusLine().getStatusCode());
                if (statusCode.startsWith("4") || statusCode.startsWith("5")) {
                	if(Constants.LOG_VISIBLE) Log.e(TAG, "Could not send HttpPost : " + httpPost);
                    throw new IOException("Host returned error code " + statusCode);
                }
            }

            if(Constants.LOG_VISIBLE)
            	Log.v(TAG, "HttpResponse Status : " + (statusLine != null ? statusLine.getStatusCode() : "NoStatusLine#noCode"));
            final String content = EntityUtils.toString(response.getEntity());
            if(Constants.LOG_VISIBLE)
            	Log.v(TAG, "HttpResponse Content : " + content.substring(0, Math.min(content.length(), 200)));

        } else {
        	if(Constants.LOG_VISIBLE)
        		Log.v(TAG, "HTTP no Response!!");
        }
	}

	private HttpPost getHttpPost(URL reportUrl, Map<String, String> parameters) throws UnsupportedEncodingException {		

	        final HttpPost httpPost = new HttpPost(reportUrl.toString());

	        if(Constants.LOG_VISIBLE)
	        	Log.v(TAG, "Setting httpPost headers");
	        
	        httpPost.setHeader("User-Agent", "Android");
	        httpPost.setHeader("Accept", "text/html,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
	        httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");

	        final String paramsAsString = getParamsAsString(parameters);
	        httpPost.setEntity(new StringEntity(paramsAsString, "UTF-8"));

	        return httpPost;
	}
	private String getParamsAsString(Map<?,?> parameters) throws UnsupportedEncodingException {
	
		final StringBuilder dataBfr = new StringBuilder();
		for (final Object key : parameters.keySet()) {
			if (dataBfr.length() != 0) {
				dataBfr.append('&');
			}
			final Object preliminaryValue = parameters.get(key);
	        final Object value = (preliminaryValue == null) ? "" : preliminaryValue;
			dataBfr.append(URLEncoder.encode(key.toString(), "UTF-8"));
	        dataBfr.append('=');
	        dataBfr.append(URLEncoder.encode(value.toString(), "UTF-8"));
		}
	
	    return dataBfr.toString();
	}


	public void sendPUT(URL url, Map<String, String> parameters) throws ParseException, IOException{
		HttpPut mHPut = new HttpPut(url.toString());
		mHPut.setHeader("User-Agent", "Android");
		mHPut.setHeader("Accept", "text/html,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
		mHPut.setHeader("Content-Type", "application/atom+xml");

        String paramsAsString;
		try {
			paramsAsString = getParamsAsString(parameters);
			mHPut.setEntity(new StringEntity(paramsAsString, "UTF-8"));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		HttpResponse response = null;
		try {
			response = httpClient.execute(mHPut, new BasicHttpContext());
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        if (response != null) {
            final StatusLine statusLine = response.getStatusLine();
            if (statusLine != null) {
                final String statusCode = Integer.toString(response.getStatusLine().getStatusCode());
                if (statusCode.startsWith("4") || statusCode.startsWith("5")) {
                	if(Constants.LOG_VISIBLE)
                		Log.e(TAG, "Could not send HttpPUT : " + mHPut);
                }
            }
            if(Constants.LOG_VISIBLE)
            	Log.v(TAG, "HttpResponse Status : " + (statusLine != null ? statusLine.getStatusCode() : "NoStatusLine#noCode"));
            final String content = EntityUtils.toString(response.getEntity());
            if(Constants.LOG_VISIBLE)
            	Log.v(TAG, "HttpResponse Content : " + content.substring(0, Math.min(content.length(), 200)));

        } else {
        	if(Constants.LOG_VISIBLE)
        		Log.v(TAG, "HTTP no Response!!");
        }
	}

}